﻿﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;

public partial class _Default : System.Web.UI.Page {

    public int numDropped = 0;
    public int numBlocked = 0;

    public int getExponential(double lambda, Random generator) {
        double num = 0;
        double rand = generator.NextDouble();
        int i = 0;

        while (num < rand) {
            num = 1 - Math.Pow(Math.E, (1 / -lambda) * i);
            i++;
        }

        return i - 1;
    }

    public int getPoisson(double lambda, Random generator) {
        double p = 1.0, L = Math.Exp(-lambda);
        int k = 0;
        do {
            k++;
            p *= generator.NextDouble();
        }
        while (p > L);
        return k - 1;
    }

    protected void Page_Load(object sender, EventArgs e) {
    }

    int callID = 800;
    public ArrayList getNewCalls(Random rndNum) {
        ArrayList newCallList = new ArrayList();

        int sum = 0;
        while (sum < Convert.ToInt32(txtLengthTime.Text)) {
            int num = getPoisson(Convert.ToInt32(txtMeanArrival.Text), rndNum);

            if (sum + num >= Convert.ToInt32(txtLengthTime.Text))
                break;

            callTypes c = callTypes.newCall;

            int callHoldingTime = getExponential(Convert.ToDouble(txtMeanCallHold.Text), rndNum);
            int callResTime = getExponential(Convert.ToDouble(txtMeanCellRes.Text), rndNum);

            int exitTime = 0;

            if (callHoldingTime > callResTime)
                exitTime = callResTime;
            else if (callResTime > callHoldingTime)
                exitTime = callResTime;
            else
                exitTime = callHoldingTime;


            // Create the call object and add it to the newCallList
            Call call = new Call(++callID, c, sum + num, (sum + num) + exitTime);

            newCallList.Add(call);
            sum += num;
        }

        return newCallList;
    }

    public ArrayList getHandoffCalls(Random rndNum) {
        ArrayList handoffCallList = new ArrayList();

        int sum = 0;
        callID += 100;
        while (sum < Convert.ToInt32(txtLengthTime.Text)) {
            int num = getPoisson(Convert.ToInt32(txtMeanArrival.Text), rndNum);

            if (sum + num >= Convert.ToInt32(txtLengthTime.Text))
                break;

            callTypes c = callTypes.handoffCall;

            double u = Convert.ToDouble(txtMeanCallHold.Text);
            double h = Convert.ToDouble(txtMeanCellRes.Text);

            double lambda = Convert.ToDouble(txtMeanArrival.Text);

            int callHoldingTime = getExponential((u/h)*lambda, rndNum);
            int callResTime = getExponential(Convert.ToDouble(txtMeanCellRes.Text), rndNum);

            int exitTime = 0;

            if (callHoldingTime > callResTime)
                exitTime = callResTime;
            else if (callResTime > callHoldingTime)
                exitTime = callResTime;
            else
                exitTime = callHoldingTime;

            // Create the call object and add it to the handoffCallList
            Call call = new Call(++callID, c, sum + num, (sum + num) + exitTime);

            handoffCallList.Add(call);
            sum += num;
        }

        return handoffCallList;
    }

    protected void btnGo_click(object sender, EventArgs e) {
        if (!Page.IsValid) {
            result.Text = "Error";
            return;
        }

        if ( !chkDebug.Checked) {
            topout.Visible = false;
            result.Visible = false;
            output.Visible = false;
        }

        ArrayList newCallList = new ArrayList();
        ArrayList handoffCallList = new ArrayList();

        ArrayList callList = new ArrayList();
        Random rndNum = new Random();

        newCallList = getNewCalls(rndNum);
        handoffCallList = getHandoffCalls(rndNum);

        // Sort both call lists by enter time
        newCallList.Sort();
        handoffCallList.Sort();

        // Print out the new call list, the handoff call list and the master call list
        result.Text = "<b>New Calls (Sorted):</b><br />";
        for (int i = 0; i < newCallList.Count; i++) {
            Call c = new Call();
            c = (Call)newCallList[i];
            result.Text += c.ToString() + "<br />";
        }
        result.Text += "<br /><b>HandOff Calls (Sorted):</b><br />";
        for (int i = 0; i < handoffCallList.Count; i++) {
            Call c = new Call();
            c = (Call)handoffCallList[i];
            result.Text += c.ToString() + "<br />";
        }

        // Add both the newCallList and handoffCallList to the master list (callList)
        callList.AddRange(newCallList);
        callList.AddRange(handoffCallList);

        callList.Sort();

        result.Text += "<br /><b>All Calls (Sorted):</b><br />";
        for (int i = 0; i < callList.Count; i++) {
            Call c = new Call();
            c = (Call)callList[i];
            result.Text += c.ToString() + "<br />";
        }
     
        // Just print out the sorted times 
        /*for (int i = 0; i < newCallList.Count; i++) {
            Call temp = (Call)newCallList[i];
            result.Text += Convert.ToString(temp.getEnterTime() + ", ");
            result.Text += "<br />";
        }*/

        Cell ap1 = new Cell(Convert.ToInt32(txtChannelCapacity.Text), Convert.ToInt32(txtReserveChan.Text), Convert.ToInt32(txtQueuesize.Text));

        ArrayList totals = new ArrayList();
        for (int sum = 0; sum <= Convert.ToInt32(txtLengthTime.Text); sum++) {
            output.Text += "BFsum: " + sum + " inUse: " + ap1.inUse + " Queue: " + ap1.queue + " Guard: " + ap1.guard;

            for (int i = 0; i < callList.Count; i++) {
                Call call = (Call)callList[i];
                if ( call.getEnterTime() > sum)
                    break;

                //if (ap1.guardEnable()) { // if doing queue algorithm
                    if (!ap1.isFull() ) {
                        output.Text += " MG_" + ap1.moveCallGuard_handToreg(sum);
                        //1) put both all calls into main allocation
                    }
                    //check queue algorithm
                    //2) if there is a handoff call, put that in a queue
                    //3) once there's space in the main allocation, move call from queue and put in allocation
                    //4) queued calls have higher priority than the regular calls
                //}

                //if (ap1.queueEnabled()) { // if Queusize is specified, then do
                    if (!ap1.isFull()) { //check guard algorithm
                        output.Text += " MQ_" + ap1.moveCallQueue_handToreg(sum);
                        //0) move reserve channels to main allocation
                    }
                    //1) chnls rnt full, put handoff & new call in main
                    //2) if main channels are full, put handoff in reserve
                    //3) if main channels are full, drop new calls
                //}

                if (sum == call.getEnterTime() ) {
                    if ( ap1.isFull() ) {
                        
                        if (call.CallType == Convert.ToInt32(callTypes.handoffCall) ) {
                            //old call algo
                            if (ap1.queueEnabled() && ap1.guardEnable() ) {
                                if (ap1.addCallGuard(sum)) {
                                    output.Text += " cID:" + call.Id + "_CA-GUA";
                                } else if (ap1.addCallQueue(sum)) {
                                    output.Text += " cID:" + call.Id + "_CA-QUE";
                                } else {
                                    dropCall(i, callList, "QUE");
                                }
                            }else if (ap1.queueEnabled() ) {
                                if (ap1.addCallQueue(sum)) {
                                    output.Text += " cID:" + call.Id + "_CA-QUE";
                                } else {
                                    dropCall(i, callList, "QUE");
                                }
                            } else if (ap1.guardEnable()) {
                                if (ap1.addCallGuard(sum)) {
                                    output.Text += " cID:" + call.Id + "_CA-GUA";
                                } else {
                                    dropCall(i, callList, "GUA");
                                }
                            } else {
                                dropCall(i, callList, "DROP");
                            }
                        } else if (call.CallType == Convert.ToInt32(callTypes.newCall) ) {
                            //new call algo
                            dropCall(i, callList, "DROP");
                        }
                    } else {
                        ap1.addCall();
                        output.Text += " cID:" + call.Id + "_CA-REG ";
                    }
                }
                if (sum == call.getExitTime() ) {
                    ap1.removeCall();
					output.Text += " cID:" + call.Id + "_rem";
					callList.RemoveAt(i);
				}
            }

            output.Text += "<br />AFsum: " + sum + " inUse: " + ap1.inUse + " Queue: " + ap1.queue + " Guard: " + ap1.guard;
            output.Text += "<br />";
            ArrayList set = new ArrayList();
            set.Add(ap1.inUse);
            set.Add(ap1.queue);
            set.Add(ap1.guard);
            totals.Add(set);
        } //end sum for loop
        
        ArrayList que = ap1.rtnQueueTrack();
        ArrayList exit = ap1.rtnExitTrack();

        topout.Text = "Times listed here!<br />";

        for (int i = 0; i < que.Count; i++) {
            topout.Text += "i:" + i;
            topout.Text += " n " + (int)que[i];
            if (exit.Count > i) {
                topout.Text += "x " + (int)exit[i];
            }
            topout.Text += "<br />";
        }

        string data = "";

        int maxNum = 0;
        int sumWaitTimes = 0;
        int count = 0;

        for (int i = 0; i < exit.Count;i++) {
            data += Convert.ToString((int)exit[i] - (int)que[i]);

            sumWaitTimes += (int)exit[i] - (int)que[i];
            count++;

            if ((int)exit[i] - (int)que[i] > maxNum)
                maxNum = (int)exit[i] - (int)que[i];

            if ((i + 1) != exit.Count) {
                data += ",";
            }

        }
        int avg;
        if (count > 0)
            avg = sumWaitTimes / count;
        else
            avg = 0;

        string gchartURL = "http://chart.apis.google.com/chart?chxr=0,0,56.667&chxs=0,676767,11.5,0,lt,676767&chxt=x&chs=400x300&cht=lc";
        
        string chd = "&chd=t:";
        string chd1 = "";
        string chd2 = "";
        string chd3 = "";
        string chls = "&chls=1";
        for (int a = 0; a < totals.Count; a++) {
            ArrayList set = (ArrayList)totals[a];
            //for (int b = 0; b < set.Count; b++) {
                chd1 = set[0]+",";
                chd2 = set[1]+",";
                chd3 = set[2]+",";
            //}
            //chd += "|";
            //chls += "|1";
        }

        gchart.Text = gchartURL + chd + chd1 + chd2 + chd3 + chls;
        topout.Text += Convert.ToString("Number of blocked calls:" + numBlocked + " Total number of new calls:" + newCallList.Count);
        topout.Text += "<br />";
        //result.Text += Convert.ToString("<br />Number of dropped calls:" + numDropped + " Total number of handoff calls:" + handoffCallList.Count);
        
        double percentBlocked = (double)numBlocked / (double)newCallList.Count;
        double percentDropped = (double)numDropped / (double)handoffCallList.Count;

        double overallBlocked = (double)numBlocked / (double)(newCallList.Count + handoffCallList.Count);
        double overallDropped = (double)numDropped / (double)(newCallList.Count + handoffCallList.Count);

        //result.Text += "<br />Percent Blocked=" + overallBlocked + " Percent Dropped: " + overallDropped + " The rest: " + Convert.ToString(1 - (overallBlocked + overallDropped));

        double success = 1 - ((double)overallBlocked + (double)overallDropped);

        string url = "https://chart.googleapis.com/chart?cht=p3&chtt=Call Behaviour&chs=380x160&chco=000099,990000,009900&chd=t:" + overallBlocked + "," + overallDropped + "," + Convert.ToString(1 - (overallBlocked + overallDropped)) + "&chdl=Blocked: " + (overallBlocked * 100).ToString("N2") + "%|Dropped: " + (overallDropped * 100).ToString("N2") + "%|Success: " + (success * 100).ToString("N2") + "%";
        imgPie.ImageUrl = url;

        string meterURL = "http://chart.googleapis.com/chart?cht=gom&chtt=Overall Performance&chs=400x190&chxt=x,y&chxl=0:|Level|1:|Poor|Adequete|Ideal&chd=t:" + success * 100;
        imgMeter.ImageUrl = meterURL;

        string avgURL = "http://chart.googleapis.com/chart?cht=lc&chs=250x150&chxt=y,x&chm=o,0066FF,0,-1,6&chd=t:" + data + "&chxr=0,0," + (maxNum + 1) + "|1,2," + txtLengthTime.Text + "&chtt=Queue+Waiting+Times|Average=" + Convert.ToString(avg) + "&chds=0," + (maxNum + 1);
        imgAvg.ImageUrl = avgURL;
    }

    protected void dropCall(int i, ArrayList callList, string append) {
        Call c = (Call)callList[i];

        if ((callTypes)c.getCallType() == callTypes.newCall)
            numBlocked++;
        else
            numDropped++;

        output.Text += " cID:" + c.Id + "_" + append;
        callList.RemoveAt(i);
    }

    protected void txtChannelCapacity_TextChanged(object sender, EventArgs e) {

    }
}